
#
# A Production Rule System implemented in Python.
#
# (C) Copyright 2006-2008, Johan Lindberg
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
# Place, Suite 330, Boston, MA 02111-1307 USA
#

CONFLICT_RESOLUTION_STRATEGIES = ['depth', 'breadth', 'lex', 'mea']

class ConflictResolver(object):
    def __init__(self, rule_engine):
        """
        ConflictResolver is used to construct an Agenda from a Conflict Set.
        """
        
        self.rule_engine = rule_engine
        self._strategy = CONFLICT_RESOLUTION_STRATEGIES[0]
        
    def get_strategy(self):
        """
        The conflict resolution strategy is used to order the conflict set.
        There are 4 available strategies: 'depth', 'breadth', 'lex' and
        'mea'.
        
        The conflict set is the unordered list of activations from all
        rules (productions). An ordered conflict set is called an agenda.
        """
        
        return self._strategy
        
    def set_strategy(self, strategy):
        if strategy.lower() not in CONFLICT_RESOLUTION_STRATEGIES:
            raise Exception("'%s' is not a supported conflict resolution strategy" % (strategy))
            
        self._strategy = strategy.lower()
        
    strategy = property(get_strategy, set_strategy)
    
    def resolve(self, conflict_set):
        try:
            impl = getattr(self, self._strategy)
            
        except AttributeError:
            raise Exception("'%s' has no implementation" % (self._strategy))
            
        return impl(conflict_set)
        
        
    ### Conflict Resolution strategies
    ### ------------------------------
    def depth(self, conflict_set):
        """
        Implements the conflict resolution strategy "Depth".
        
        The most recently triggered Activation with the highest Salience is
        executed first. Activations with equal Salience and Recency are
        triggered in some unspecified (not random) order.
        """
        
        result = []
        agenda = self._group_by_salience(conflict_set)
        for salience in sorted(agenda.keys(), reverse = True):
            result += sorted(agenda[salience],
                             key = lambda activation: activation[2].timestamp,
                             reverse = True)
                             
        return result
        
    def breadth(self, conflict_set):
        """
        Implements the conflict resolution strategy "Breadth".
        
        The Activation with the highest Salience that was triggered first is
        executed first. Activations with equal Salience and Recency are
        triggered in some unspecified (not random) order.
        """
        
        result = []
        agenda = self._group_by_salience(conflict_set)
        for salience in sorted(agenda.keys(), reverse = True):
            result += sorted(agenda[salience],
                             key = lambda activation: activation[2].timestamp,
                             reverse = False)
                             
        return result
        
    def lex(self, conflict_set):
        raise NotImplementedException("The lex strategy has not been implemented yet!")
        
    def mea(self, conflict_set):
        raise NotImplementedException("The mea strategy has not been implemented yet!")
        
        
    def _group_by_salience(self, conflict_set):
        """
        """
        
        result = {}
        for rule, salience, tokens, rhs, locals in conflict_set:
            for token in tokens:
                try:
                    result[salience].append((rule, salience, token, rhs, locals))
                    
                except KeyError:
                    result[salience] = [(rule, salience, token, rhs, locals),]
                    
        return result
        
        